using System;
using Castle.Core.Logging;
using Castle.Facilities.NHibernateIntegration;
using Castle.MicroKernel;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using NHibernate;
using DreamDriven.ObjectModel.Exceptions;

namespace DreamDriven.ObjectModel
{
    /// <summary>
    /// Represents a domain model.
    /// </summary>
    public abstract class BaseDomainModel : IDomainModel
    {
        private readonly IWindsorContainer _container = new WindsorContainer();
        private readonly Type _type;
        private readonly ISessionManager _sessionManager;
        private string _serviceName;

        /// <summary>
        /// NHibernate session. Implemented explictlly for encapsulation NHibernate
        /// Session from Business Logic enterprise layer.
        /// </summary>
        /// <value>Session.</value>
        ISession ISessionContainer.Session
        {
            get
            {
                return _container.Resolve<ISession>();
            }
        }

        public ILogger Logger { get; private set; }

        public string ServiceName
        {
            get { return _serviceName; }
            set
            {
                _serviceName = value;
                Logger.Info("{0} service has started.", _serviceName);
            }
        }

        public IWindsorContainer Container
        {
            get { return _container; }
        }

        /// <summary>
        /// Creates the NHibernate session, factory method.
        /// </summary>
        /// <returns></returns>
        protected internal virtual ISession OpenSession()
        {
            return _sessionManager.OpenSession();
        }

        /// <summary>
        /// Gets the business logic manager of the specified type.
        /// </summary>
        /// <typeparam name="T">The type of the business logic manager.</typeparam>
        /// <returns>An instance of the business logic manager with the specified type.</returns>
        public virtual T GetBusinessLogicManager<T>() 
            where T : IBusinessLogicManager
        {
            try
            {
                return _container.Resolve<T>();
            }
            catch (ComponentNotFoundException)
            {
                var exception = new BusinessLogicManagerNotRegistredException(typeof (T));
                string message = string.Format("Business logic manager '{0}' is not registred in domain model '{1}'",
                                               typeof (T).Name, _type);
                Logger.Fatal(message, exception);
                throw exception;
            }
        }
       
        /// <summary>
        /// Gets the repository.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public virtual T GetRepository<T>()
            where T: IDomainRepository
        {
            try
            {
                return _container.Resolve<T>();
            }
            catch (ComponentNotFoundException)
            {
                throw new RepositoryNotRegistredException(typeof(T));
            }
        }

        /// <summary>
        /// Registers the repository.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public void RegisterRepository<T>()
            where T : IDomainRepository
        {           
            _container.Register(Component.For<T>());
            Logger.Debug("Repository {0} for domain model {1} registred.", typeof (T).Name, _type);
        }

        /// <summary>
        /// Registers a business logic manager with the domain model.
        /// </summary>
        public void RegisterBusinessLogicManager<T>()
            where T:class, IBusinessLogicManager
        {           
            _container.Register(Component.For<T>().DependsOn(Component.For<ILogger>()));
            Logger.Debug("BusinessLogicManager {0} for domain model {1} registred.", typeof(T).Name, _type);
        }

        /// <summary>
        /// Initializes a new instance of a child of the <see cref="BaseDomainModel"/> class.
        /// </summary>
        protected BaseDomainModel(ISessionManager sessionManager, ILogger logger)
        {
            _type = GetType();
            _container.Register(Component.For<ILogger>().Instance(logger));
            _container.Register(Component.For<ISession>().UsingFactoryMethod(OpenSession).LifeStyle.PerThread);
            _container.Register(Component.For<IDomainModel>().Instance(this));

            _sessionManager = sessionManager;
            Logger = logger;
        }
    }
}